package com.xd.core.business.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xd.common.constant.SysConstant;
import com.xd.common.exception.XkdException;
import com.xd.core.business.entity.CarCredit;
import com.xd.core.business.mapper.CarCreditMapper;
import com.xd.core.business.service.ICarCreditService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xd.core.h5.vo.CarInsuranceDetail;
import com.xd.core.business.vo.RepaymentInsuranceSchedule;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.io.*;
import java.util.zip.ZipFile;

/**
 * <p>
 * 保单贷车辆信息 服务实现类
 * </p>
 *
 * @author ZX
 * @since 2025-06-19
 */
@Slf4j
@Service
public class CarCreditServiceImpl extends ServiceImpl<CarCreditMapper, CarCredit> implements ICarCreditService {


    private static final int maxCount = 50;
    @Autowired
    private CarCreditMapper carCreditMapper;
    @Value("${image.carCredit}")
    private String carCredit;//上传保单贷车辆信息
    @Value("${image.localPathDir}")
    private String localPathDir;

    // 尝试的编码列表
    private static final List<Charset> CHARSET_TRIALS = Arrays.asList(
            StandardCharsets.UTF_8,
            Charset.forName("GBK"),
            Charset.forName("ISO-8859-1")
    );

    @Transactional
    @Override
    public TreeMap<String, Integer> importCarCreditListener(List<CarCredit> list ,Integer incomingId) {
            if (CollectionUtils.isEmpty(list))
                throw new XkdException("导入车辆信息不能为空！");
        //同一批次数据去重
        List<String> carNos = list.stream().map(CarCredit::getCarNo).distinct().collect(Collectors.toList());
        //对同一进件id中的车辆信息数据去重
        List<CarCredit> carCredits = carCreditMapper.selectList(new LambdaQueryWrapper<CarCredit>().eq(CarCredit::getIncomingId, incomingId));
        if (CollectionUtils.isNotEmpty(carCredits)) {
            List<String> oldCarNos = carCredits.stream().map(CarCredit::getCarNo).collect(Collectors.toList());
            //取交集
            List<String> newList = oldCarNos.stream().filter(carNos::contains).collect(Collectors.toList());

            list = list.stream().filter(a ->
                 !newList.contains(a.getCarNo())
            ).collect(Collectors.toList());
        }
        //分批次插入
    int count = 0;//计数器
    ArrayList<CarCredit> newList = new ArrayList<>();
    for (CarCredit c : list) {
        newList.add(c);
        count++;
        if (count % maxCount == 0) {
            carCreditMapper.importCarCredit(list,incomingId);
            newList.clear();
        }
    }
        carCreditMapper.importCarCredit(list,incomingId);
    TreeMap<String, Integer> map = new TreeMap<>();
    map.put("导入成功",list.size());
    return map;
        }

    @Override
    @Transactional
    public void processDrivingZipFile(MultipartFile file,Integer incomingId)   {
        if (file == null || file.isEmpty()) {
            log.error("上传文件为空");
            throw new XkdException("上传文件不能为空");
        }

        Path tempDir = null;
        try {
            // 创建临时目录
            tempDir = createTempDirectory();
            log.debug("创建临时目录: {}", tempDir);

            // 保存ZIP文件到临时目录
            Path zipFilePath = saveZipFileToTempDir(file, tempDir);

            // 尝试使用多种编码解压ZIP文件
            unzipFile(zipFilePath, tempDir);

            // 处理解压后的文件
            processExtractedDrivingFiles(tempDir,incomingId);

            log.info("行驶证批量上传成功，共处理 {} 个文件", getFileCount(tempDir));
        } catch (Exception e) {
            log.error("行驶证批量上传失败", e);
            throw new XkdException("行驶证批量上传失败: " + e.getMessage());
        } finally {
            // 清理临时目录
            cleanTempDirectory(tempDir);
        }
    }

    @Override
    public void processInsuranceAplZipFile(MultipartFile file, Integer incomingId) {
        if (file == null || file.isEmpty()) {
            log.error("上传文件为空");
            throw new XkdException("上传文件不能为空");
        }

        Path tempDir = null;
        try {
            // 创建临时目录
            tempDir = createTempDirectory();
            log.debug("创建临时目录: {}", tempDir);

            // 保存ZIP文件到临时目录
            Path zipFilePath = saveZipFileToTempDir(file, tempDir);

            // 尝试使用多种编码解压ZIP文件
            unzipFile(zipFilePath, tempDir);

            // 处理解压后的文件
            processExtractedInsuranceFiles(tempDir,incomingId);

            log.info("投保单批量上传成功，共处理 {} 个文件", getFileCount(tempDir));
        } catch (Exception e) {
            log.error("投保单批量上传失败", e);
            throw new XkdException("投保单批量上传失败: " + e.getMessage());
        } finally {
            // 清理临时目录
            cleanTempDirectory(tempDir);
        }
    }

    @Override
    public BigDecimal commissionAmount(CarInsuranceDetail carInsuranceDetail) {
        Integer loanTerm = carInsuranceDetail.getTerm();
        Integer repayDay = carInsuranceDetail.getRepayDay();
        String loanMethod = carInsuranceDetail.getLoanMethod();
        BigDecimal loanAmount = carInsuranceDetail.getInsuranceAmount(); //商业保险总金额
        BigDecimal annualRate = carInsuranceDetail.getPostServiceFeeRate().multiply(new BigDecimal(12)
                .divide(BigDecimal.valueOf(loanTerm),10, RoundingMode.HALF_UP)
                );//后置服务费年利率
        BigDecimal totalInterest = BigDecimal.ZERO;

        BigDecimal remainingPrincipal = loanAmount; // 剩余本金

        // 累计利息
        BigDecimal totalInterestAcc = BigDecimal.ZERO;

        // 获取当前日期
        Calendar calendar = Calendar.getInstance();
        Date currentDate = new Date(calendar.getTimeInMillis());

        // 计算首次还款日期
        calendar.setTime(currentDate);
        calendar.add(Calendar.MONTH, 1);
        calendar.set(Calendar.DAY_OF_MONTH, repayDay);
        switch (loanMethod) {
            // 等额本金
            case SysConstant.REPAY_TYPE_ZERO: {
                // 日利率 = 年利率 / 36000
                BigDecimal dayRate = annualRate.divide(BigDecimal.valueOf(36000), 10, RoundingMode.HALF_UP);

                // 月利率 = 年利率 / 1200
                BigDecimal monthlyRate = annualRate.divide(BigDecimal.valueOf(1200), 10, RoundingMode.HALF_UP);

                // 每月应还本金 = 贷款本金 / 总还款月数
                BigDecimal monthlyPrincipal = loanAmount.divide(BigDecimal.valueOf(loanTerm), 2, RoundingMode.HALF_UP);


                // 计算首月实际借款天数
                Calendar loanStartCalendar = Calendar.getInstance();
                loanStartCalendar.setTime(new Date());

                int actualDays;

                // 如果放款日是1号，则实际借款天数为30天
                if (loanStartCalendar.get(Calendar.DAY_OF_MONTH) == 1) {
                    actualDays = 30; // 固定为30天
                } else {
                    // 否则，计算从放款日到月底的实际天数
                    actualDays = loanStartCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) - loanStartCalendar.get(Calendar.DAY_OF_MONTH) + 1;
                    actualDays += repayDay; // 加上还款日
                }

                // 首月应还利息 = 剩余本金 × 日利率 × 实际借款天数
                BigDecimal firstMonthInterest = remainingPrincipal.multiply(dayRate)
                        .multiply(BigDecimal.valueOf(actualDays))
                        .setScale(2, RoundingMode.HALF_UP);
                //累计利息
                totalInterestAcc = totalInterestAcc.add(firstMonthInterest);
                totalInterest = totalInterestAcc;
                // 首月应还总额 = 每月应还本金 + 首月应还利息
                BigDecimal firstMonthPayment = monthlyPrincipal.add(firstMonthInterest);

                // 更新剩余本金
                remainingPrincipal = remainingPrincipal.subtract(monthlyPrincipal);

                // 设置首月还款日期
                Date firstRepaymentDate = new Date(calendar.getTimeInMillis());

                RepaymentInsuranceSchedule firstPayment = new RepaymentInsuranceSchedule();
                firstPayment.setAmount(firstMonthPayment);
                firstPayment.setPrincipal(monthlyPrincipal);
                firstPayment.setInterest(firstMonthInterest);
                firstPayment.setInstallmentNumber(1);
                firstPayment.setRepaymentDate(firstRepaymentDate);
                firstPayment.setCreateTime(currentDate);
                firstPayment.setPrincipalTodo(monthlyPrincipal);
                firstPayment.setInterestTodo(firstMonthInterest);

                // 按月还款，增加一个月
                calendar.add(Calendar.MONTH, 1);
                calendar.set(Calendar.DAY_OF_MONTH, repayDay);

                // 次月及以后的计算
                for (int period = 2; period <= loanTerm; period++) {
                    // 每月应还利息 = 剩余本金 × 月利率
                    BigDecimal monthlyInterest = remainingPrincipal.multiply(monthlyRate).setScale(2, RoundingMode.HALF_UP);

                    // 每月应还总额 = 每月应还本金 + 每月应还利息
                    totalInterestAcc = totalInterestAcc.add(monthlyInterest);
                    totalInterest = totalInterestAcc;

                }
                break;
            }
            // 等额本息
            case SysConstant.REPAY_TYPE_ONE: {
                // 月利率 = 年利率 / 12
                BigDecimal monthlyRate = annualRate.divide(BigDecimal.valueOf(1200), 10, RoundingMode.HALF_UP);

                // 每月还款额 X = P * [r(1+r)^n] / [(1+r)^n - 1]
                BigDecimal numerator = monthlyRate.multiply(BigDecimal.ONE.add(monthlyRate).pow(loanTerm));
                BigDecimal denominator = BigDecimal.ONE.add(monthlyRate).pow(loanTerm).subtract(BigDecimal.ONE);
                BigDecimal monthlyPayment = loanAmount.multiply(numerator).divide(denominator, 2, RoundingMode.HALF_UP);

                // 总还款额 = 每月还款额 * 总还款月数
                BigDecimal totalRepayment = monthlyPayment.multiply(BigDecimal.valueOf(loanTerm));

                // 总利息 = 总还款额 - 贷款本金
                totalInterest = totalRepayment.subtract(loanAmount);

                break;
            }
            // 等本等息
            case SysConstant.REPAY_TYPE_TWO: {
                // 月利率 = 年利率 / 12
                BigDecimal monthlyRate = annualRate.divide(BigDecimal.valueOf(1200), 10, RoundingMode.HALF_UP);

                // 计算首月实际借款天数
                Calendar loanStartCalendar = Calendar.getInstance();
                loanStartCalendar.setTime(new Date());

                int actualDays;

                // 如果放款日是1号，则实际借款天数为30天
                if (loanStartCalendar.get(Calendar.DAY_OF_MONTH) == 1) {
                    actualDays = 30; // 固定为30天
                } else {
                    // 否则，计算从放款日到月底的实际天数
                    actualDays = loanStartCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) - loanStartCalendar.get(Calendar.DAY_OF_MONTH) + 1;
                    actualDays += repayDay; // 加上还款日
                }

                log.info("月利率"+monthlyRate);
                // 总利息 = 保险总金额 × 月利率 × (还款期数-（(30-首月)/30）)
                totalInterest = loanAmount.multiply(monthlyRate)
                        .multiply(BigDecimal.valueOf(loanTerm).subtract(
                                BigDecimal.valueOf(30).subtract(BigDecimal.valueOf(actualDays))
                                        .divide(BigDecimal.valueOf(30), 10, RoundingMode.HALF_UP)
                        ))
                        .setScale(2, RoundingMode.HALF_UP);

                break;
            }
        }
            return totalInterest;
    }

    private void processExtractedInsuranceFiles(Path tempDir, Integer incomingId) throws IOException {
        // 定义文件命名模式
        Pattern pattern = Pattern.compile("(投保单)(\\d*)-(.*)\\.(jpg|png|pdf)", Pattern.CASE_INSENSITIVE);
        Files.walk(tempDir)
                .filter(Files::isRegularFile)
                .forEach(filePath -> {
                    try {
                        String fileName = filePath.getFileName().toString();
                        Matcher matcher = pattern.matcher(fileName);
                        if (matcher.matches()) {
                            String fileType = matcher.group(1);
                            int fileNumber = matcher.group(2).isEmpty() ? 1 : Integer.parseInt(matcher.group(2));
                            String carNo = matcher.group(3);
                            processInsuranceFile(filePath, carNo, fileType, fileNumber,incomingId);
                        } else {
                            log.warn("文件命名格式不正确: {}", fileName);
                        }
                    } catch (Exception e) {
                        log.error("处理文件失败: {}", filePath, e);
                    }
                });
    }


    private Path createTempDirectory() throws IOException {
        return Files.createTempDirectory(Paths.get(localPathDir+carCredit),"upload-");
    }

    private Path saveZipFileToTempDir(MultipartFile file, Path tempDir) throws IOException {
        Path zipFilePath = tempDir.resolve(file.getOriginalFilename());
        Files.copy(file.getInputStream(), zipFilePath, StandardCopyOption.REPLACE_EXISTING);
        return zipFilePath;
    }
    private void unzipFile(Path zipFilePath, Path destDir) {
        // 尝试多种编码
        for (Charset charset : CHARSET_TRIALS) {
            try {
                log.info("尝试使用 {} 编码解压ZIP文件", charset.name());
                doUnzipFile(zipFilePath, destDir, charset);
                log.info("使用 {} 编码成功解压ZIP文件", charset.name());
                return; // 成功解压后返回
            } catch (Exception e) {
                log.warn("使用 {} 编码解压失败: {}", charset.name(), e.getMessage());
                // 尝试下一个编码
            }
        }

        // 所有编码尝试都失败
        throw new XkdException("无法解压ZIP文件，尝试了所有支持的编码");
    }

    private void doUnzipFile(Path zipFilePath, Path destDir, Charset charset) throws IOException {
        try (ZipFile zipFile = new ZipFile(zipFilePath.toFile(), charset)) {
            Enumeration<? extends ZipEntry> entries = zipFile.entries();

            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();

                // 检查是否为文件夹
                if (entry.isDirectory()) {
                    log.error("压缩包中包含文件夹: {}", entry.getName());
                    throw new XkdException("压缩包中不能包含文件夹");
                }

                // 安全地构建目标文件路径
                Path targetPath = buildSafePath(destDir, entry.getName());

                // 创建父目录
                Files.createDirectories(targetPath.getParent());

                // 写入文件
                try (InputStream in = zipFile.getInputStream(entry);
                     OutputStream out = Files.newOutputStream(targetPath)) {

                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) != -1) {
                        out.write(buffer, 0, bytesRead);
                    }
                }

                log.debug("解压文件: {}", targetPath);
            }
        }
    }

    private String sanitizeFileName(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return "unknown_file";
        }

        // 移除或替换非法字符
        // Windows不允许的字符: \ / : * ? " < > |
        String sanitized = fileName.replaceAll("[\\\\/:*?\"<>|]", "_");

        // 处理文件名过长的情况
        if (sanitized.length() > 255) {
            int dotIndex = sanitized.lastIndexOf('.');
            if (dotIndex > 0) {
                String name = sanitized.substring(0, 200); // 保留前200个字符
                String ext = sanitized.substring(dotIndex);
                sanitized = name + ext;
            } else {
                sanitized = sanitized.substring(0, 255);
            }
        }

        return sanitized;
    }
    private Path buildSafePath(Path baseDir, String entryName) throws IOException {
        // 处理可能包含非法字符的文件名
        String sanitizedName = sanitizeFileName(entryName);

        // 构建完整路径
        Path fullPath = baseDir.resolve(sanitizedName);

        // 验证路径安全性，防止路径遍历攻击
        if (!fullPath.normalize().startsWith(baseDir.normalize())) {
            log.error("非法路径: {}", entryName);
            throw new XkdException("非法文件路径");
        }

        return fullPath;
    }

    private void processExtractedDrivingFiles(Path tempDir,Integer incomingId) throws IOException {
        // 定义文件命名模式
        Pattern pattern = Pattern.compile("(行驶证)(\\d*)-(.*)\\.(jpg|png|pdf)", Pattern.CASE_INSENSITIVE);

        Files.walk(tempDir)
                .filter(Files::isRegularFile)
                .forEach(filePath -> {
                    try {
                        String fileName = filePath.getFileName().toString();
                        Matcher matcher = pattern.matcher(fileName);
                        if (matcher.matches()) {
                            String fileType = matcher.group(1);
                            int fileNumber = matcher.group(2).isEmpty() ? 1 : Integer.parseInt(matcher.group(2));
                            String carNo = matcher.group(3);
                            processDrivingFile(filePath, carNo, fileType, fileNumber,incomingId);
                        } else {
                            log.warn("文件命名格式不正确: {}", fileName);
                        }
                    } catch (Exception e) {
                        log.error("处理文件失败: {}", filePath, e);
                    }
                });
    }

    private void processInsuranceFile(Path filePath, String carNo, String fileType, int fileNumber, Integer incomingId) {
        try {
            // 从数据库获取车辆信息
            CarCredit carCredit = carCreditMapper.selectOne(
                    new LambdaQueryWrapper<CarCredit>().eq(CarCredit::getCarNo, carNo).eq(CarCredit::getIncomingId,incomingId));
            if (ObjectUtil.isNotEmpty(carCredit)){
                // 保存文件到永久存储位置
                String fileUrl = saveFileToPermanentLocation(filePath, carNo, fileType, fileNumber,carCredit.getCarId());

                // 更新车辆信息
                updateCarCreditFileUrl(carCredit, fileType, fileUrl, fileNumber);

                // 保存到数据库
                carCreditMapper.updateInsurance(carCredit);

                log.info("成功处理文件: {}, 车牌号: {}, 文件类型: {}, 编号: {}",
                        filePath.getFileName(), carNo, fileType, fileNumber);
            }
        } catch (Exception e) {
            throw  new XkdException("处理文件失败");
        }
    }

    private void processDrivingFile(Path filePath, String carNo, String fileType, int fileNumber,Integer incomingId) {
        try {
            System.out.println("----------------");
            // 从数据库获取车辆信息
            CarCredit carCredit = carCreditMapper.selectOne(
                    new LambdaQueryWrapper<CarCredit>().eq(CarCredit::getCarNo, carNo).eq(CarCredit::getIncomingId,incomingId));
            if (ObjectUtil.isNotEmpty(carCredit)){
                // 保存文件到永久存储位置
                String fileUrl = saveFileToPermanentLocation(filePath, carNo, fileType, fileNumber,carCredit.getCarId());

                // 更新车辆信息
                updateCarCreditFileUrl(carCredit, fileType, fileUrl, fileNumber);

                // 保存到数据库
                carCreditMapper.updateDriving(carCredit);

                log.info("成功处理文件: {}, 车牌号: {}, 文件类型: {}, 编号: {}",
                        filePath.getFileName(), carNo, fileType, fileNumber);
            }
        } catch (Exception e) {
            throw  new XkdException("处理文件失败");
        }
    }



    private String saveFileToPermanentLocation(Path sourcePath, String carNo, String fileType, int fileNumber,Integer incomingId) throws IOException {
        // 创建按车牌号分类的目录
        Path targetDir = Paths.get(localPathDir+carCredit,incomingId+"", carNo);
        Files.createDirectories(targetDir);

        // 构建目标文件名
        String fileName = String.format("%s%d-%s.%s",
                fileType,
                fileNumber,
                carNo,
                getFileExtension(sourcePath.getFileName().toString()));

        Path targetPath = targetDir.resolve(fileName);

        // 移动文件（使用REPLACE_EXISTING选项允许覆盖同名文件）
        Files.move(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);

        // 返回可访问的URL
        return Paths.get(incomingId+"", carNo, fileName).toString();
    }

    private void updateCarCreditFileUrl(CarCredit carCredit1, String fileType, String newUrl, int fileNumber) {
        // 解析现有URLs
        Map<Integer, String> urlMap = new HashMap<>();
        if ("行驶证".equals(fileType)){
            urlMap = parseUrlMap(carCredit1.getDrivingUrl());
        }else {
            urlMap = parseUrlMap(carCredit1.getInsuranceUrl());
        }
        // 更新或添加URL
        urlMap.put(fileNumber,carCredit+"/"+newUrl);

        // 构建新的URL字符串
        String updatedUrls = buildUrlString(urlMap);

        // 更新车辆信息
        if ("行驶证".equals(fileType)){
            carCredit1.setDrivingUrl(updatedUrls);
        }else {
            carCredit1.setInsuranceUrl(updatedUrls);
        }
    }


    private Map<Integer, String> parseUrlMap(String urlString) {
        Map<Integer, String> urlMap = new HashMap<>();
        if (urlString == null || urlString.isEmpty()) {
            return urlMap;
        }

        String[] urls = urlString.split(",");
        Pattern pattern = Pattern.compile("/(.*?)(\\d?)-(.*?)\\.(jpg|png|pdf)$", Pattern.CASE_INSENSITIVE);

        for (String url : urls) {
            Matcher matcher = pattern.matcher(url);
            if (matcher.find()) {
                String numberStr = matcher.group(2);
                int number = numberStr.isEmpty() ? 1 : Integer.parseInt(numberStr);
                urlMap.put(number, url);
            } else {
                // 默认编号为1
                urlMap.put(1, url);
            }
        }
        return urlMap;
    }

    private String buildUrlString(Map<Integer, String> urlMap) {
        if (urlMap.isEmpty()) {
            return null;
        }

        // 按编号排序
        List<Map.Entry<Integer, String>> entries = new ArrayList<>(urlMap.entrySet());
        entries.sort(Map.Entry.comparingByKey());

        // 构建逗号分隔的URL字符串
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<Integer, String> entry : entries) {
            if (sb.length() > 0) {
                sb.append(",");
            }
            sb.append(entry.getValue());
        }

        return sb.toString();
    }

    private String getFileExtension(String fileName) {
        int dotIndex = fileName.lastIndexOf('.');
        return dotIndex > 0 ? fileName.substring(dotIndex + 1).toLowerCase() : "";
    }

    private long getFileCount(Path dir) throws IOException {
        return Files.walk(dir)
                .filter(Files::isRegularFile)
                .count();
    }

    private void cleanTempDirectory(Path tempDir) {
        if (tempDir == null) {
            return;
        }
        try {
            Files.walk(tempDir)
                    .sorted(Comparator.reverseOrder())
                    .map(Path::toFile)
                    .forEach(File::delete);
            log.debug("成功清理临时目录: {}", tempDir);
        } catch (IOException e) {
            log.error("清理临时目录失败: {}", tempDir, e);
        }
    }
}

